#region License, Terms and Conditions
//
// Prawks - Copyright (c) 2007 Atif Aziz. All rights reserved.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 2.1 of the License, or (at your option)
// any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
// details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
//
#endregion

namespace Prawks.DbMethods
{
    using System;
    using System.Data;

    /// <summary>
    /// When overridden in a derived class, specifies how a parameter is to
    /// be defined in a command object.
    /// </summary>

    [ AttributeUsage(AttributeTargets.Parameter) ]
    public abstract class DbParameterAttribute : Attribute
    {
        private string _name;
        private int _size;
        private byte _precision;
        private byte _scale;
        private ParameterDirection _direction;
        private bool _directionDefined;

        /// <summary>
        /// Initializes a new instance of the <see cref="DbParameterAttribute"/> class.
        /// </summary>

        protected DbParameterAttribute() {}

        /// <summary>
        /// Initializes a new instance of the <see cref="DbParameterAttribute"/> class
        /// with the parameter name.
        /// </summary>

        protected DbParameterAttribute(string name)
        {
            Name = name;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DbParameterAttribute"/> class
        /// with the parameter size.
        /// </summary>

        protected DbParameterAttribute(int size)
        {
            Size = size;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DbParameterAttribute"/> class
        /// with the parameter name and size.
        /// </summary>

        protected DbParameterAttribute(string name, int size)
        {
            Name = name;
            Size = size;
        }

        /// <summary>
        /// Gets or sets the name of the parameter.
        /// </summary>

        public string Name
        {
            get { return _name == null ? string.Empty : _name; }
            set { _name = value; }
        }

        /// <summary>
        /// Gets or sets the direction of the parameter.
        /// </summary>

        public ParameterDirection Direction
        {
            get
            {
                if (!_directionDefined)
                    throw new InvalidOperationException("Value is undefined.");

                return _direction;
            }

            set 
            { 
                _direction = value; 
                _directionDefined = true;
            }
        }

        /// <summary>
        /// Gets or sets the size of the parameter.
        /// </summary>

        public int Size
        {
            get { return _size; }
            set { _size = value; }
        }

        /// <summary>
        /// Gets or sets the precision for a numeric parameter.
        /// </summary>

        public byte Precision
        {
            get { return _precision; }
            set { _precision = value; }
        }

        /// <summary>
        /// Gets or sets the scale for a numeric parameter.
        /// </summary>

        public byte Scale
        {
            get { return _scale; }
            set { _scale = value; }
        }

        /// <summary>
        /// Indicates whether the name is defined or not.
        /// </summary>

        public bool IsNameDefined
        {
            get { return _name != null && _name.Length != 0; }
        }

        /// <summary>
        /// Indicates whether the direction is defined or not.
        /// </summary>

        public bool IsDirectionDefined
        {
            get { return _directionDefined; }
        }

        /// <summary>
        /// Indicates whether the size is defined or not.
        /// </summary>

        public bool IsSizeDefined
        {
            get { return _size != 0; }
        }

        /// <summary>
        /// Indicates whether the scale is defined or not.
        /// </summary>

        public bool IsScaleDefined
        {
            get { return _scale != 0; }
        }

        /// <summary>
        /// Indicates whether the precision is defined or not.
        /// </summary>

        public bool IsPrecisionDefined
        {
            get { return _precision != 0; }
        }

        /// <summary>
        /// Configure a command parameter using those properties from this instance
        /// that were defined.
        /// </summary>
        /// <param name="dbParameter">
        /// The command parameter to be configured.
        /// </param>
        /// <param name="defaultName">
        /// The default name to configure if <see cref="IsNameDefined"/> returns
        /// <b>false</b>.
        /// </param>

        protected void ConfigureParameter(IDbDataParameter dbParameter, string defaultName)
        {
            //
            // Validate parameters
            //

            if (dbParameter == null)
                throw new ArgumentNullException("dbParameter");
            
            if (defaultName == null)
                throw new ArgumentNullException("defaultName");

            if (defaultName.Length == 0)
                throw new ArgumentOutOfRangeException("defaultName", "Value cannot be an empty string.");

            //
            // Setup the command parameter object using settings from the 
            // attribute. Use only those values that were defined.
            //

            dbParameter.ParameterName = IsNameDefined ? Name : defaultName;

            if (IsSizeDefined)
                dbParameter.Size = Size;

            if (IsPrecisionDefined)
                dbParameter.Precision = Precision;

            if (IsScaleDefined)
                dbParameter.Scale = Scale;

            if (IsDirectionDefined)
                dbParameter.Direction = Direction;
        }
    }
}
